library SoundSystem;

uses lists, VectorGeometry, CoreI, ResourceManagerI, SoundSystemI, fmod, fmodtypes, fmoderrors, fmodpresets, sysutils, common, sdl;
type PSound=^TSound;
TSound=packed record
handle:SoundHandle;
sound:Fmod_Sound;
channel:Fmod_Channel;
state:SoundState;
position:TVector3F;
DefaultFreq:single;
end;

const
MaxChannels=1000;
                                                  var
listener:record
position:TVector3F;
end;
FS:Fmod_System=nil;
                                                  sounds:PList=nil;
MaxHandle:SoundHandle=0;
ResourceManager:IResourceManager;
Function VectorToFmod_Vector(const v:TVector3F):Fmod_Vector;
begin
result.x := trunc(v[0] * 100) / 100;
result.y := trunc(v[1] * 100) / 100;
result.z := trunc(v[2] * 100) / 100;
end;
procedure errcheck(code:FMOD_Result; msg:string='');
var s:string;
begin
if code <> FMOD_OK then begin
s:=format('Sound system: %s, fmod error %d (%s)', [msg, longint(code), fmod_errorstring(code)]);
Core_Error(PChar(s), ECore_Main);
end;
end;

Function UpdateHandler(Event:TSDL_Event):boolean; cdecl;
begin
result:=true;
errcheck(fmod_system_update(fs), 'SoundSystem Update: fmod_system_update failed');
         end;

         Procedure PauseAll(const state:boolean); cdecl;
var PWalk:PList;
s:PSound;
begin
PWalk:=sounds;
while PWalk <> nil do begin
s:=PWalk^.data;
if s^.state=ss_playing then
errcheck(FMOD_Channel_SetPaused(s^.channel, state), 'SS_Pause: FMOD_Channel_SetPaused failed');
PWalk := PWalk^.Next;
end;
end;

Function CreateGeometryBox(const Pos,_size:TVector3F; DirectOcclusion, ReverbOcclusion:single):SS_Geometry; cdecl;
var v:array[1..6] of array[1..4] of fmod_vector;
pi,i:integer;
g:fmod_geometry;
FPos:Fmod_Vector;
size:fmod_vector;
begin
size:=VectorToFmod_Vector(_size);
v[1,1].x:=-size.x / 2;
v[1,1].y:=-size.y / 2;
v[1,1].z:=-size.z / 2;
v[1,2].x:=-size.x / 2;
v[1,2].y:=size.y / 2;
v[1,2].z:=-size.z / 2;
v[1,3].x:=size.x / 2;
v[1,3].y:=size.y / 2;
v[1,3].z:=-size.z / 2;
v[1,4].x:=size.x / 2;
v[1,4].y:=-size.y / 2;
v[1,4].z:=-size.z / 2;

v[3,1].x:=-size.x / 2;
v[3,1].y:=-size.y / 2;
v[3,1].z:=size.z / 2;
v[3,2].x:=-size.x / 2;
v[3,2].y:=size.y / 2;
v[3,2].z:=size.z / 2;
v[3,3].x:=size.x / 2;
v[3,3].y:=size.y / 2;
v[3,3].z:=size.z / 2;
v[3,4].x:=size.x / 2;
v[3,4].y:=-size.y / 2;
v[3,4].z:=size.z / 2;

v[2,1].x:=-size.x / 2;
v[2,1].y:=-size.y / 2;
v[2,1].z:=-size.z / 2;
v[2,2].x:=-size.x / 2;
v[2,2].y:=-size.y / 2;
v[2,2].z:=size.z / 2;
v[2,3].x:=-size.x / 2;
v[2,3].y:=size.y / 2;
v[2,3].z:=size.z / 2;
v[2,4].x:=-size.x / 2;
v[2,4].y:=size.y / 2;
v[2,4].z:=-size.z / 2;

v[4,1].x:=size.x / 2;
v[4,1].y:=-size.y / 2;
v[4,1].z:=-size.z / 2;
v[4,2].x:=size.x / 2;
v[4,2].y:=-size.y / 2;
v[4,2].z:=size.z / 2;
v[4,3].x:=size.x / 2;
v[4,3].y:=size.y / 2;
v[4,3].z:=size.z / 2;
v[4,4].x:=size.x / 2;
v[4,4].y:=size.y / 2;
v[4,4].z:=-size.z / 2;

v[5,1].x:=-size.x / 2;
v[5,1].y:=-size.y / 2;
v[5,1].z:=-size.z / 2;
v[5,2].x:=-size.x / 2;
v[5,2].y:=-size.y / 2;
v[5,2].z:=size.z / 2;
v[5,3].x:=size.x / 2;
v[5,3].y:=-size.y / 2;
v[5,3].z:=size.z / 2;
v[5,4].x:=size.x / 2;
v[5,4].y:=-size.y / 2;
v[5,4].z:=-size.z / 2;

v[6,1].x:=-size.x / 2;
v[6,1].y:=size.y / 2;
v[6,1].z:=-size.z / 2;
v[6,2].x:=-size.x / 2;
v[6,2].y:=size.y / 2;
v[6,2].z:=size.z / 2;
v[6,3].x:=size.x / 2;
v[6,3].y:=size.y / 2;
v[6,3].z:=size.z / 2;
v[6,4].x:=size.x / 2;
v[6,4].y:=size.y / 2;
v[6,4].z:=-size.z / 2;
errcheck(fmod_system_creategeometry(fs, 6, 24, g), 'SoundSystem.CreateGeometryBox: FMOD_System_CreateGeometry failed');
try
FPos:=VectorToFmod_Vector(pos);
for i:=1 to 6 do
errcheck(fmod_geometry_addpolygon(g, directOcclusion, reverbOcclusion, true,4, @v[i,1], pi),
'SoundSystem.CreateGeometryBox: FMOD_Geometry_AddPolygon failed');
ErrCheck(fmod_geometry_setposition(g, FPos), 'SoundSystem.CreateGeometryBox: FMOD_Geometry_SetPosition failed');
except
fmod_geometry_release(g);
raise;
end;
result:=g;
end;

Procedure ToggleGeometry(const Geometry:SS_Geometry; state:boolean); cdecl;
                                               begin
errcheck(fmod_geometry_setactive(geometry, state), 'SoundSystem.ToggleGeometry: FMOD_Geometry_Setactive failed');
end;
Procedure ReleaseGeometry(const geometry:SS_Geometry); cdecl;
begin
ToggleGeometry(geometry, false);
errcheck(fmod_geometry_release(geometry), 'SoundSystem.ReleaseGeometry: FMOD_Geometry_Release failed');
end;

Function strToFmod_Reverb_Properties(const reverb:pchar):FMOD_REVERB_PROPERTIES;
var s:string;
begin
                                                       s:=ansiuppercase(reverb);
                                                         if s = 'OFF' then result := FSOUND_PRESET_OFF else
  if s = 'GENERIC' then result := FSOUND_PRESET_GENERIC else
  if s = 'PADDEDCELL' then result := FSOUND_PRESET_PADDEDCELL else
  if s = 'ROOM' then result := FSOUND_PRESET_ROOM else
  if s = 'BATHROOM' then result := FSOUND_PRESET_BATHROOM else
  if s = 'LIVINGROOM' then result := FSOUND_PRESET_LIVINGROOM else
  if s = 'STONEROOM' then result := FSOUND_PRESET_STONEROOM else
  if s = 'AUDITORIUM' then result := FSOUND_PRESET_AUDITORIUM else
  if s = 'CONCERTHALL' then result := FSOUND_PRESET_CONCERTHALL else
  if s = 'CAVE' then result := FSOUND_PRESET_CAVE else
  if s = 'ARENA' then result := FSOUND_PRESET_ARENA else
  if s = 'HANGAR' then result := FSOUND_PRESET_HANGAR else
  if s = 'CARPETTEDHALLWAY' then result := FSOUND_PRESET_CARPETTEDHALLWAY else
  if s = 'HALLWAY' then result := FSOUND_PRESET_HALLWAY else
  if s = 'STONECORRIDOR' then result := FSOUND_PRESET_STONECORRIDOR else
  if s = 'ALLEY' then result := FSOUND_PRESET_ALLEY else
  if s = 'FOREST' then result := FSOUND_PRESET_FOREST else
  if s = 'CITY' then result := FSOUND_PRESET_CITY else
  if s = 'MOUNTAINS' then result := FSOUND_PRESET_MOUNTAINS else
  if s = 'QUARRY' then result := FSOUND_PRESET_QUARRY else
  if s = 'PLAIN' then result := FSOUND_PRESET_PLAIN else
  if s = 'PARKINGLOT' then result := FSOUND_PRESET_PARKINGLOT else
  if s = 'SEWERPIPE' then result := FSOUND_PRESET_SEWERPIPE else
  if s = 'UNDERWATER' then result := FSOUND_PRESET_UNDERWATER else
  if s = 'DRUGGED' then result := FSOUND_PRESET_DRUGGED else
  if s = 'DIZZY' then result := FSOUND_PRESET_DIZZY else
  if s = 'PSYCHOTIC' then result := FSOUND_PRESET_PSYCHOTIC else
  result:=FSOUND_PRESET_OFF;
  end;

Procedure SetAmbientReverb(const reverb:PChar); cdecl;
var prop:FMOD_REVERB_PROPERTIES;
begin
prop := strToFMOD_REVERB_Properties(reverb);
errcheck(FMOD_System_SetReverbAmbientProperties(fs, prop),
'SoundSystem.SetAmbientReverb: FMOD_System_SetReverbAmbientProperties failed');
end;

Function CreateReverb(const reverb:PChar; pos:TVector3F; min,max:single):ss_reverb; cdecl;
var prop:FMOD_REVERB_PROPERTIES;
begin
                                            prop := strToFMOD_REVERB_Properties(reverb);
                                            ErrCheck(Fmod_System_CreateReverb(fs, result), 'Fmod_System_CreateReverb failed');
ErrCheck(FMOD_Reverb_Set3dAttributes(result, VectorToFmod_Vector(pos), min, max), 'FMOD_Reverb_Set3dAttributes failed');
ErrCheck(FMOD_Reverb_SetProperties(result, prop), 'FMOD_Reverb_SetProperties failed');
                                            end;

                                            Procedure ReleaseReverb(const reverb:SS_Reverb); cdecl;
begin
                                            errcheck(FMOD_Reverb_Release(reverb), 'FMOD_Reverb_Release failed');
                                            end;
  function GetSoundByHandle(const handle:SoundHandle):PSound;
var PWalk:PList;
begin
result:=nil;
if handle=0 then exit;
PWalk:=Sounds;
while PWalk <> nil do begin
if PSound(PWalk^.data)^.handle=handle then begin
result:=PWalk^.data;
break;
end;
PWalk := PWalk^.Next;
end;
end;

function Init3DSound(const RawData:pointer; const RawDataSize:cardinal; var FreeRawData:boolean; const name:PChar):pointer; cdecl;
var fex:fmod_createsoundexinfo;
begin
result:=nil;
fillchar(fex, sizeof(fex), 0);
fex.cbsize:=sizeof(fex);
fex.length:=RawDatasize;
errcheck(fmod_system_createsound(fs, RawData, fmod_3d or fmod_software or fmod_openmemory{ or FMOD_3D_LINEARROLLOFF}, @fex, result),
'SoundSystem.CreateSound3D failed');
end;

function Init2DSound(const RawData:pointer; const RawDataSize:cardinal; var FreeRawData:boolean; const name:PChar):pointer; cdecl;
var fex:fmod_createsoundexinfo;
begin
result:=nil;
fillchar(fex, sizeof(fex), 0);
fex.cbsize:=sizeof(fex);
fex.length:=RawDataSize;
errcheck(fmod_system_createsound(fs, RawData, fmod_2d or fmod_openmemory, @fex, result),
'SoundSystem.CreateSound2D failed');
end;

function Init2DStreamedSound(const RawData:pointer; const RawDataSize:cardinal; var FreeRawData:boolean; const name:PChar):pointer; cdecl;
var fex:fmod_createsoundexinfo;
begin
result:=nil;
fillchar(fex, sizeof(fex), 0);
fex.cbsize:=sizeof(fex);
fex.length:=RawDataSize;
errcheck(fmod_system_createsound(fs, RawData, fmod_2d or fmod_openmemory or fmod_createstream, @fex, result),
'SoundSystem.CreateSound2DStreamed: fmod_system_createsound failed');
end;
Procedure FreeSound(Data:pointer); cdecl;
begin
Fmod_Sound_Release(data);
end;

Function PlaySound3D(const name:PChar;  pos:TVector3F; Paused:boolean):SoundHandle; cdecl;
var s:PSound;
sound:pointer;
c:pointer;
v:fmod_vector;
begin
result:=0;
sound := ResourceManager^.GetResource(name, rt_3DSound);
if (sound=nil) or (ResourceManager^.GetResourceType(sound) <> RT_3DSound) then begin
Core_Error(PChar('SoundSystem.PlaySound3D: resource '+name+' not found'), ECore_Main);
exit;
end;
errcheck(fmod_system_playsound(fs, fmod_channel_free, ResourceManager^.GetResourceData(sound), true, c),
'SoundSystem.PlaySound3D: FMOD_System_PlaySound failed');
new(s);
errcheck(FMOD_Channel_GetFrequency(c, s^.defaultFreq), 'SoundSystem.PlaySound3D: FMOD_Channel_GetFrequency failed');
v:=VectorToFmod_Vector(pos);
errcheck(fmod_channel_setmode(c, fmod_loop_normal), 'SoundSystem.PlaySound3D: FMOD_Channel_SetMode failed');
errcheck(fmod_channel_set3dattributes(c,v, pfmod_vector(nil)^),
'SoundSystem.PlaySound3D: FMOD_Channel_Set3DPosition failed');
s^.position:=pos;
if not paused then errcheck(FMOD_Channel_SetPaused(c, false), 'SoundSystem.PlaySound3D: FMOD_Channel_SetPaused failed');
s^.sound:=sound;
s^.channel:=c;
if paused then s^.state:=ss_paused else s^.state:=ss_playing;
inc(maxhandle);
s^.handle:=maxhandle;
errcheck(fmod_channel_setuserdata(c, list_add(sounds, s)), 'SoundSystem.PlaySound3d: fmod_channel_setuserdata failed');
result:=s^.handle;
if result=0 then begin
list_remove(sounds, s);
dispose(s);
FMOD_Channel_stop(c);
end;
end;
Function PlaySound2D(const name:PChar;  Paused:boolean):SoundHandle; cdecl;
var s:PSound;
sound:pointer;
c:pointer;
begin
result:=0;
sound := ResourceManager^.GetResource(name);
if (sound=nil) or (ResourceManager^.GetResourceType(sound) <> RT_2DSound) then begin
Core_Error(PChar('SoundSystem.PlaySound2D: resource '+name+' not found'), ECore_Main);
exit;
end;
errcheck(fmod_system_playsound(fs, fmod_channel_free, ResourceManager^.GetResourceData(sound), true, c),
'SoundSystem.PlaySound2D: FMOD_System_PlaySound failed');
new(s);
errcheck(FMOD_Channel_GetFrequency(c, s^.defaultFreq), 'SoundSystem.PlaySound2D: FMOD_Channel_GetFrequency failed');
errcheck(fmod_channel_setmode(c, fmod_loop_normal), 'SoundSystem.PlaySound3D: FMOD_Channel_SetMode failed');
if not paused then errcheck(FMOD_Channel_SetPaused(c, false), 'SoundSystem.PlaySound3D: FMOD_Channel_SetPaused failed');
s^.sound:=sound;
s^.channel:=c;
if paused then s^.state:=ss_paused else s^.state:=ss_playing;
inc(maxhandle);
s^.handle:=maxhandle;
errcheck(fmod_channel_setuserdata(c, list_add(sounds, s)), 'SoundSystem.PlaySound2D: fmod_channel_setuserdata failed');
result:=s^.handle;
if result=0 then begin
list_remove(sounds, s);
dispose(s);
FMOD_Channel_stop(c);
end;
end;

function EndCallback(channel: FMOD_CHANNEL; callbacktype: FMOD_CHANNEL_CALLBACKTYPE; command: Integer; commanddata1:word; commanddata2:word ): FMOD_RESULT; stdcall;
var l:plist;
begin
result:=fmod_ok;
if callbacktype <> fmod_channel_callbacktype_end then exit;
l:=nil;
errcheck(fmod_channel_getuserdata(channel, pointer(l)), 'Sound system: EndCallback: fmod_channel_getuserdata failed');
if l<>nil then begin
try
list_removenode(sounds, l);
fmod_channel_setuserdata(channel, nil);
             except
on e:exception do
log_write(PChar('soundSystem.EndCallback: error: ' + e.Message), ll_error);
end;
end;
end;

Function PlaySound3DOnce(const handle:SoundHandle; name:PChar; pos:TVector3F):SoundHandle; cdecl;
var s:PSound;
sound:pointer;
c:pointer;
v:fmod_vector;
begin
result:=0;
       sound:=ResourceManager^.GetResource(name);
if (sound=nil) or (ResourceManager^.GetResourceType(sound) <> RT_3DSound) then begin
Core_Error(PChar('SoundSystem.PlaySound3Donce: resource '+name+' not found'), ECore_Main);
exit;
end;
s:=GetSoundByHandle(handle);
if s <> nil then begin
c:=s^.channel;
errcheck(fmod_system_playsound(fs, fmod_channel_reuse, ResourceManager^.GetResourceData(sound), true, c),
'SoundSystem.PlaySound3DOnce: FMOD_System_PlaySound failed');
end else begin
errcheck(fmod_system_playsound(fs, fmod_channel_free, ResourceManager^.GetResourceData(sound), true, c),
'SoundSystem.PlaySound3DOnce: FMOD_System_PlaySound failed');
new(s);
inc(MaxHandle);
s^.handle:=MaxHandle;
errcheck(fmod_channel_setuserdata(c, list_add(sounds, s)), 'SoundSystem_PlaySound3dOnce: fmod_channel_setuserdata failed');
end;
errcheck(FMOD_Channel_GetFrequency(c, s^.defaultFreq), 'SoundSystem.PlaySound3DOnce: FMOD_Channel_GetFrequency failed');
v:=VectorToFmod_Vector(pos);
errcheck(fmod_channel_set3dattributes(c,v, pfmod_vector(nil)^), 'SoundSystem.PlaySound3DOnce: FMOD_Channel_Set3DAttributes failed');
s^.position:=pos;
errcheck(fmod_channel_setcallback(c, fmod_channel_callbacktype_end, EndCallback, 0), 'SoundSystem.PlaySound3DOnce: FMOD_Channel_SetCallback failed');
errcheck(FMOD_Channel_SetPaused(c, false), 'SoundSystem.PlaySound3DOnce: FMOD_Channel_SetPaused failed');
s^.sound:=sound;
s^.channel:=c;
s^.state:=ss_playing;
result:=s^.handle;
if result=0 then begin
list_remove(sounds, s);
dispose(s);
FMOD_Channel_stop(c);
end;
end;

Function PlaySound2DOnce(const handle:SoundHandle; name:PChar):SoundHandle; cdecl;
var s:PSound;
sound:pointer;
c:pointer;
begin
result:=0;
       sound:=ResourceManager^.GetResource(name);
if (sound=nil) or (ResourceManager^.GetResourceType(sound) <> RT_2DSound) then begin
Core_Error(PChar('SoundSystem.PlaySound3Donce: resource '+name+' not found'), ECore_Main);
exit;
end;
s:=GetSoundByHandle(handle);
if s <> nil then begin
c:=s^.channel;
errcheck(fmod_system_playsound(fs, fmod_channel_reuse, ResourceManager^.GetResourceData(sound), true, c),
'SoundSystem.PlaySound2DOnce: FMOD_System_PlaySound failed');
end else begin
errcheck(fmod_system_playsound(fs, fmod_channel_free, ResourceManager^.GetResourceData(sound), false, c),
'SoundSystem.PlaySound2DOnce: FMOD_System_PlaySound failed');
new(s);
inc(MaxHandle);
s^.handle:=MaxHandle;
errcheck(FMOD_Channel_GetFrequency(c, s^.defaultFreq), 'SoundSystem.PlaySound2DOnce: FMOD_Channel_GetFrequency failed');
errcheck(fmod_channel_setuserdata(c, list_add(sounds, s)), 'SoundSystem_PlaySound2DOnce: fmod_channel_setuserdata failed');
end;
errcheck(fmod_channel_setcallback(c, fmod_channel_callbacktype_end, @EndCallback, 0), 'SoundSystem.PlaySound3DOnce: FMOD_Channel_SetCallback failed');
s^.sound:=sound;
s^.channel:=c;
s^.state:=ss_playing;
result:=s^.handle;
if result=0 then begin
list_remove(sounds, s);
dispose(s);
FMOD_Channel_stop(c);
end;
end;

Function GetSoundFrequency(handle:SoundHandle):Single; cdecl;
var s:PSound;
f:single;
begin
             s := GetSoundByHandle(handle);
if s <> nil then begin
errcheck(FMOD_Channel_GetFrequency(s^.channel, f), 'SoundSystem.GetSoundFrequency: FMOD_Channel_GetFrequency failed');
             result:= f / (s^.DefaultFreq / 100);
             end else begin
log_write('SoundSystem.GetSoundFrequency warning: trying to change parameters of non-existing sound.', ll_debug);
             result:=0;
             end;
             end;

             Procedure SetSoundFrequency(handle:SoundHandle; percent:single); cdecl;
var s:PSound;
             begin
             s := GetSoundByHandle(handle);
                      if s <> nil then
errcheck(FMOD_Channel_SetFrequency(S^.channel, s^.DefaultFreq / 100 * percent), 'SoundSystem.SetSoundFrequency: FMOD_Channel_SetFrequency failed')
else log_write('SoundSystem.SetSoundFrequency warning: trying to change parameters of non-existing sound.', ll_debug);
             end;

             Procedure                  SetListenerPos(const pos:TVector3F); cdecl;
var v:fmod_vector;
begin
v:=VectorToFmod_Vector(pos);
errcheck(fmod_system_set3dlistenerattributes(fs, 0, v, pfmod_vector(nil)^, pfmod_vector(nil)^, pfmod_vector(nil)^),
'SoundSystem.SetListenerPos: FMOD_System_Set3DListenerAttributes failed');
listener.position:=pos;
end;

                                                      Function GetListenerPos:TVector3F; cdecl;
                                                      begin
                                                      result:=listener.position;
                                                      end;

Procedure SetListenerVelocity(const velocity:TVector3F); cdecl;
var v:fmod_vector;
begin
v:=VectorToFmod_Vector(Velocity);
errcheck(fmod_system_set3dlistenerattributes(fs, 0, pfmod_vector(nil)^, v, pfmod_vector(nil)^, pfmod_vector(nil)^),
'SoundSystem.SetListenerVelocity: FMOD_System_Set3DListenerAttributes failed');
                            end;

                            Procedure SetListenerOrientation(const forward,top:TVector3F); cdecl;
                            var v1,v2:fmod_vector;
                            begin
v1:=VectorToFmod_Vector(forward);
v2:=VectorToFmod_Vector(top);
  errcheck(fmod_system_set3dlistenerattributes(fs, 0, pfmod_vector(nil)^,pfmod_vector(nil)^, v1, v2),
'SoundSystem.SetListenerOrientation: FMOD_System_Set3DListenerAttributes failed');
end;

procedure SetSoundPos(handle:SoundHandle; pos:TVector3F; HeadRelative:boolean); cdecl;
var v:fmod_vector;
s:PSound;
fm:fmod_mode;
begin
s:=GetSoundByHandle(handle);
if s=nil then begin
log_write('SoundSystem.SetSoundPos warning: trying to change parameters of non-existing sound.', ll_debug);
exit;
end;
v:=VectorToFmod_Vector(pos);
errcheck(fmod_channel_set3dattributes(s^.channel, v, pfmod_vector(nil)^), 'SoundSystem.SetSoundPos: FMOD_channel_Set3DAttributes failed');
s^.position := pos;
errcheck(fmod_channel_Getmode(s^.channel, fm), 'SoundSystem.SetSoundPos: FMOD_Channel_GetMode failed');
fm:=fm and ($ffffffff xor (fmod_3d_headrelative or fmod_3d_worldrelative));
if HeadRelative then fm:=fm or fmod_3d_headrelative else fm:=fm or fmod_3d_worldrelative;
errcheck(fmod_channel_setmode(s^.channel, fm), 'SoundSystem.SetSoundPos: FMOD_Channel_SetMode failed');
end;

Procedure MoveSound(const Handle:SoundHandle; v:TVector3F); cdecl;
var s:PSound;
fv:Fmod_vector;
begin
s:=getsoundbyhandle(handle);
if s= nil then begin
log_write('SoundSystem.MoveSound warning: trying to change parameters of non-existing sound', ll_warn);
exit;
end;
s^.position :=vectorAdd(s^.position, v);
fv:=VectorToFmod_Vector(s^.position);
errcheck(fmod_channel_set3dattributes(s^.channel, fv, pfmod_vector(nil)^), 'SoundSystem.MoveSound: FMOD_channel_Set3DAttributes failed');
end;

Procedure SetSoundMinMaxDistance(Handle:SoundHandle; min,max:single); cdecl;
var s:PSound;
begin
s:=GetSoundByHandle(handle);
if s=nil then begin
log_write('SoundSystem.SetSoundMinMaxDistance warning: non-existing sound handle', ll_warn);
exit;
end;
errcheck(fmod_channel_set3dminmaxdistance(s^.channel,min,max), 'SoundSystem.SetSoundMinMaxDistance: FMOD_Channel_Set3DMinMaxDistance failed');
end;

Procedure SetSoundVelocity(const Handle:SoundHandle; velocity:TVector3F); cdecl;
var v:fmod_vector;
s:PSound;
begin
s:=GetSoundByHandle(handle);
if s=nil then begin
log_write('SoundSystem.SetSoundVelocity warning: trying to change parameters of non-existing sound.', ll_debug);
exit;
end;
v:=VectorToFmod_Vector(velocity);
errcheck(fmod_channel_set3dattributes(s^.channel, pfmod_vector(nil)^, v), 'SoundSystem.SetSoundVelocity: FMOD_channel_Set3DAttributes failed');
end;

procedure SetGeometryWorldSize(const size:single); cdecl;
begin
errcheck(FMOD_System_SetGeometrySettings(fs, size), 'SoundSystem.SetGeometryWorldSize: FMOD_System_SetGeometrySettings failed');
end;

Procedure PauseSound(Handle:SoundHandle; state:boolean); cdecl;
var ss:PSound;
begin
ss:=getsoundbyhandle(handle);
if ss=nil then begin
log_write('SoundSystem.PauseSound warning: trying to change parameters of non-existing sound.', ll_debug);
exit;
end;
if state then ss^.state:=ss_paused else ss^.state:=ss_playing;
errcheck(FMOD_Channel_SetPaused(ss^.channel, state), 'SoundSystem.PauseSound: FMOD_Channel_SetPaused failed');
end;

function GetSoundState(Handle:SoundHandle):SoundState;
var s:PSound;
begin
result:=ss_none;
s:=GetSoundByHandle(handle);
if s=nil then begin
log_write('SoundSystem.GetSoundState warning: trying to get state of non-existing channel', ll_warn);
exit;
end;
result:=s^.state;
end;
Function IsPlaying(Handle:SoundHandle):boolean; cdecl;
begin
result:=getsoundstate(handle)=ss_playing;
end;

Procedure StopSound(Handle:SoundHandle); cdecl;
var s:PSound;
p:PList;
begin
s:=getsoundbyhandle(handle);
if s=nil then begin
log_write('SoundSystem.StopSound warning: trying to stop non-existing sound', ll_warn);
exit;
end;
errcheck(fmod_channel_getuserdata(s^.channel, pointer(p)), 'SoundSystem.StopSound: FMOD_Channel_GetUserData failed');
errcheck(fmod_channel_setuserdata(s^.channel, nil), 'SoundSystem.StopSound: FMOD_Channel_SetUserData failed');
errcheck(FMOD_Channel_Stop(s^.channel), 'SoundSystem.StopSound: FMOD_Channel_Stop failed');
list_remove(sounds, s);
dispose(s);
end;

Procedure SetGeometryPos(const Geometry:ss_Geometry; pos:TVector3F); cdecl;
var v:Fmod_Vector;
begin
v:=VectorToFmod_Vector(pos);
errcheck(fmod_geometry_setposition(geometry, v), 'SoundSystem.SetGeometryPos: fmod_geometry_setposition failed');
end;

procedure SetGeometryRotation(const Geometry:SS_Geometry; forward,top:TVector3F); cdecl;
var f,t:Fmod_Vector;
begin
                    f:=VectorToFmod_Vector(forward);
t:=VectorToFmod_Vector(top);
errcheck(fmod_geometry_setRotation(geometry, f,t), 'SoundSystem.SetGeometryRotation: fmod_geometry_setRotation failed');
                    end;

                    Function GetSoundPos(handle:SoundHandle):TVector3F; cdecl;
var s:PSound;
begin
s:=getsoundbyhandle(handle);
if s = nil then result:=AffineVectorMake(0,0,0)
else result:=s^.position;
end;

procedure MoveListener(const v:TVector3F); cdecl;
begin
SetListenerPos(VectorAdd(listener.position, v));
    end;
var PluginInfo:PPluginInfo=nil;
Procedure Init; cdecl;
var v:cardinal;
                                begin
ResourceManager:=Core_RequirePlugin(ResourceManagerI.ResourceManagerGUID);
ResourceManager^.AddResourceHandler(rt_3DSound, Init3DSound, FreeSound);
ResourceManager^.AddResourceHandler(rt_2DSound, Init2DSound, FreeSound);
errcheck(fmod_system_create(fs), 'Plugin_Init: Unable to create fmod_system object');
try
errcheck(fmod_system_getversion(fs, v), 'Plugin_Init: FMOD_System_GetVersion failed');
if v < fmod_version then
Core_Error(PChar(Format('Sound system: Plugin_Init: unable to initialize FMOD, newer version of fmodex.dll is required. Current version %x.%x.%x, required %x.%x.%x',
[(v and $ffff0000) shr 16, (v and $ff00) shr 8, v and $ff, (fmod_version and $ffff0000) shr 16, (fmod_version and $ff00) shr 8, fmod_version and $ff])), ECore_Main);
errcheck(fmod_system_init(fs, MaxChannels, fmod_init_normal or fmod_init_software_occlusion or  FMOD_INIT_SOFTWARE_HRTF  or FMOD_INIT_VOL0_BECOMES_VIRTUAL, nil), 'SS_Init: FMOD_system_init failed');
Log_writeF('Using FMOD version: %x.%x.%x', ll_info, [(v and $ffff0000) shr 16, (v and $ff00) shr 8, v and $ff]);
except
fmod_system_release(fs);
fs := nil;
raise;
end;
Core_RegisterEventHandler(Event_MainLoop, UpdateHandler);
                                end;

Procedure Free; cdecl;
begin
dispose(ISoundSystem(PluginInfo^.InterfaceStruct));
PluginInfo:=PPluginInfo(cardinal(PluginInfo) + sizeof(TPluginInfo));
dispose(ISoundGeometry(PluginInfo^.interfaceStruct));
PluginInfo:=PPluginInfo(cardinal(PluginInfo) + sizeof(TPluginInfo));
dispose(IReverb(PluginInfo^.interfaceStruct));
PluginInfo:=pointer(cardinal(plugininfo) -sizeof(TPluginInfo) * 2);
freemem(PluginInfo, sizeof(TPluginInfo) * 3);
List_FreeAll(Sounds);
fmod_system_release(fs);
fs := nil;
end;

Procedure Plugin_GetInfo(var info:PPluginInfo; var length:cardinal); cdecl;
begin
length:=3;
getmem(PluginInfo, sizeof(TPluginInfo) *length);
info:=PluginInfo;
FillChar(PluginInfo^, sizeof(TPluginInfo)*length, 0);
PluginInfo^.Name := 'SoundSystem_FMODEX';
PluginInfo^.GUID := SoundSystemGUID;
PluginInfo^.Init := init;
PluginInfo^.free := free;
new(ISoundSystem(PluginInfo^.InterfaceStruct));
ISoundSystem(PluginInfo^.InterfaceStruct)^.PauseAll:=PauseAll;
ISoundSystem(PluginInfo^.InterfaceStruct)^.PlaySound2D := PlaySound2D;
ISoundSystem(PluginInfo^.InterfaceStruct)^.PlaySound2DOnce := PlaySound2DOnce;
ISoundSystem(PluginInfo^.InterfaceStruct)^.PlaySound3D := PlaySound3D;
ISoundSystem(PluginInfo^.InterfaceStruct)^.PlaySound3DOnce := PlaySound3DOnce;
ISoundSystem(PluginInfo^.InterfaceStruct)^.SetListenerPos := SetListenerPos;
ISoundSystem(PluginInfo^.InterfaceStruct)^.GetListenerPos := GetListenerPos; 
ISoundSystem(PluginInfo^.InterfaceStruct)^.SetListenerVelocity := SetListenerVelocity;
ISoundSystem(PluginInfo^.InterfaceStruct)^.SetListenerOrientation := SetListenerOrientation;
ISoundSystem(PluginInfo^.InterfaceStruct)^.SetSoundPos := SetSoundPos;
ISoundSystem(PluginInfo^.InterfaceStruct)^.MoveSound := MoveSound;
ISoundSystem(PluginInfo^.InterfaceStruct)^.SetSoundVelocity := SetSoundVelocity;
ISoundSystem(PluginInfo^.InterfaceStruct)^.SetSoundMinMaxDistance := SetSoundMinMaxDistance;
ISoundSystem(PluginInfo^.InterfaceStruct)^.SetSoundFrequency := SetSoundFrequency;
ISoundSystem(PluginInfo^.InterfaceStruct)^.GetSoundFrequency := GetSoundFrequency; 
ISoundSystem(PluginInfo^.InterfaceStruct)^.PauseSound := PauseSound;
ISoundSystem(PluginInfo^.InterfaceStruct)^.GetSoundState := GetSoundState;
ISoundSystem(PluginInfo^.InterfaceStruct)^.IsPlaying := IsPlaying;
ISoundSystem(PluginInfo^.InterfaceStruct)^.StopSound := StopSound;
ISoundSystem(PluginInfo^.InterfaceStruct)^.GetSoundPos := GetSoundPos;
ISoundSystem(PluginInfo.InterfaceStruct)^.MoveListener := MoveListener;
PluginInfo:=PPluginInfo(cardinal(PluginInfo) + sizeof(TPluginInfo));
PluginInfo^.guid:=SoundGeometryGUID;
new(ISoundGeometry(PluginInfo^.interfaceStruct));
ISoundGeometry(PluginInfo^.InterfaceStruct)^.SetGeometryWorldSize := SetGeometryWorldSize;
ISoundGeometry(PluginInfo^.InterfaceStruct)^.CreateGeometryBox := CreateGeometryBox;
ISoundGeometry(PluginInfo^.InterfaceStruct)^.ToggleGeometry:=ToggleGeometry;
ISoundGeometry(PluginInfo^.InterfaceStruct)^.ReleaseGeometry:=ReleaseGeometry;
ISoundGeometry(PluginInfo^.InterfaceStruct)^.SetGeometryPos := SetGeometryPos;
ISoundGeometry(PluginInfo^.InterfaceStruct)^.SetGeometryRotation := SetGeometryRotation;
PluginInfo:=PPluginInfo(cardinal(PluginInfo) + sizeof(TPluginInfo));
PluginInfo^.GUID := ReverbGUID;
          new(IReverb(PluginInfo^.interfaceStruct));
IReverb(PluginInfo^.InterfaceStruct)^.SetAmbientReverb := SetAmbientReverb;
IReverb(PluginInfo^.InterfaceStruct)^.CreateReverb := CreateReverb;
IReverb(PluginInfo^.InterfaceStruct)^.ReleaseReverb := ReleaseReverb; 
PluginInfo:=info;
                                end;

exports Plugin_GetInfo;
                                begin
end.
